package p2p.peer;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.HashSet;
import java.util.Set;

import p2p.client.message.Message;
import p2p.command.Connect;
import p2p.command.Leave;
import p2p.command.Spawn;
import p2p.peer.client.Client;
import p2p.peer.client.ClientManager;
import p2p.util.Config;

public class Peer implements Runnable {

	private static Peer instance;

	private PeerListener listener;

	private DatagramSocket socket;

	private Set<Message> ackMessages;

	private int peerPort;
	
	private Peer() {

		
		this.ackMessages = new HashSet<Message>();

	}

	public static Peer getInstance() {

		if (instance == null) {

			instance = new Peer();

		}

		return instance;

	}

	public void createSocket(String port) {
		try {

			this.peerPort = new Integer(port);
			this.socket = new DatagramSocket(new Integer(port));

		} catch (SocketException e) {

			e.printStackTrace();

		}

	}

	public void spawn(String ip, String port) {

		this.listener.logPeerAction("Spawn: " + ip + ":" + port);
		new Thread(new Spawn(ip,Integer.parseInt(port))).start();

	}

	public void leave() {

		this.listener.logPeerAction("Leave");
		for (Client c : ClientManager.getInstance().getClients()) {
			String ip = c.getIp();
			int port = c.getPort();
			new Thread(new Leave(ip, port)).start();	
		}
		
	}

	public void connectTracker(String ip) {

		this.listener.logPeerAction("Connecting to Server");

		new Thread(new Connect(ip, Config.TRACKER_PORT)).start();

	}

	public void addPeerListener(PeerListener listener) {

		this.listener = listener;

	}

	public synchronized void addAckMessage(Message m) {

		this.ackMessages.add(m);

	}

	public synchronized void removeAckMessage(Message m) {

		this.ackMessages.remove(m);

	}

	public void listen() {

		this.listener.logPeerAction("Peer has started to listen on " + this.getPeerPort());
		
		while (true) {
			
			this.listener.logPeerAction("Test!");

			try {

				byte[] recieveData = new byte[1024];

				DatagramPacket recieve = new DatagramPacket(recieveData,
						recieveData.length);

				socket.receive(recieve);
				
				System.out.println(recieve);
				
				this.listener.logPeerAction("R: " + recieve);
				
				Message m = new Message(recieveData);
				
				this.listener.logPeerAction("Recieved Data!!!");

				if (m.isAck()) {
					// Message is an ack message. 
					for (Message msg : this.ackMessages){
						if (msg.getUuid().equals(m.getUuid())){
							msg.getCommandObject().execute();
							this.ackMessages.remove(m);
							break;
						}
					}
				}else{
					
					this.listener.logPeerAction("New Incoming Message LOG!");
					System.out.println("New Incoming Message!");
					int command = m.getCommand();
					this.listener.logPeerAction("COMMAND LOG! " + command);
					
					// LEAVE 
					if(m.getCommand() == 2){
						this.listener.logPeerAction("LEAVE COMMAND LOG! ");
					}
					// SPAWN
					else if(m.getCommand() == 1){
						this.listener.logPeerAction("LEAVE COMMAND SPAWN! ");
					}
				}

			} catch (SocketException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}

	@Override
	public void run() {

		this.listen();

	}

	public boolean isAcknowledged(Message m) {
		if (this.ackMessages.contains(m))
			return false;
		else
			return true;
	}

	public PeerListener getListener() {
		return listener;
	}
	
	public int getPeerPort() {
		return peerPort;
	}

}